<?php

declare(strict_types=1);

namespace app\service;

/**
 * 统一缓存服务类
 * 提供智能缓存管理，支持用户相关缓存的标签化管理
 * 
 * 优化目标：
 * 1. 智能缓存键命名
 * 2. 标签化管理
 * 3. 性能监控
 * 4. 错误处理
 */
class CacheService 
{
    const CACHE_PREFIX = 'oa_cache_';
    const STATS_FILE = RUNTIME_PATH . 'cache_stats.log';
    
    /**
     * 获取用户相关缓存键
     */
    public static function getUserCacheKey(string $type, int $userId): string 
    {
        return self::CACHE_PREFIX . "user_{$userId}_{$type}";
    }

    /**
     * 获取CRM客户缓存键
     */
    public static function getCustomerCacheKey(int $customerId): string 
    {
        return self::CACHE_PREFIX . "crm_customer_{$customerId}";
    }

    /**
     * 获取CRM客户列表缓存键
     */
    public static function getCustomerListCacheKey(int $page = 1, int $limit = 10, array $params = []): string 
    {
        $paramsString = !empty($params) ? '_' . md5(serialize($params)) : '';
        return self::CACHE_PREFIX . "crm_customer_list_page{$page}_limit{$limit}{$paramsString}";
    }

    /**
     * 获取缓存（本地数组作为缓存）
     */
    public static function getLocalCache(string $key) 
    {
        $cacheFile = self::getCacheFile($key);
        if (file_exists($cacheFile)) {
            $data = unserialize(file_get_contents($cacheFile));
            if ($data['expire'] === 0 || time() < $data['expire']) {
                return $data['value'];
            } else {
                unlink($cacheFile); // 过期删除
            }
        }
        return null;
    }

    /**
     * 设置缓存
     */
    public static function setLocalCache(string $key, $value, int $expire = 3600): bool 
    {
        try {
            $cacheFile = self::getCacheFile($key);
            $cacheDir = dirname($cacheFile);
            if (!is_dir($cacheDir)) {
                mkdir($cacheDir, 0755, true);
            }

            $data = [
                'value' => $value,
                'expire' => $expire > 0 ? time() + $expire : 0
            ];

            return file_put_contents($cacheFile, serialize($data)) !== false;
        } catch (\Exception $e) {
            self::logError('setLocalCache', $key, $e);
            return false;
        }
    }

    /**
     * 设置缓存（兼容测试）
     */
    public static function set(string $key, $value, int $expire = 3600): bool 
    {
        $result = self::setLocalCache($key, $value, $expire);
        if ($result) {
            self::logCacheHit('set', $key, true);
        }
        return $result;
    }

    /**
     * 获取缓存（兼容测试）
     */
    public static function get(string $key) 
    {
        $result = self::getLocalCache($key);
        self::logCacheHit('get', $key, $result !== null);
        return $result;
    }

    /**
     * 删除缓存（兼容测试）
     */
    public static function delete(string $key): bool 
    {
        $result = self::deleteLocalCache($key);
        if ($result) {
            self::logCacheHit('delete', $key, true);
        }
        return $result;
    }

    /**
     * 删除缓存
     */
    public static function deleteLocalCache(string $key): bool 
    {
        try {
            $cacheFile = self::getCacheFile($key);
            return file_exists($cacheFile) ? unlink($cacheFile) : true;
        } catch (\Exception $e) {
            self::logError('deleteLocalCache', $key, $e);
            return false;
        }
    }

    /**
     * 检查缓存是否存在
     */
    public static function hasLocalCache(string $key): bool 
    {
        $cacheFile = self::getCacheFile($key);
        if (file_exists($cacheFile)) {
            $data = unserialize(file_get_contents($cacheFile));
            if ($data['expire'] === 0 || time() < $data['expire']) {
                return true;
            } else {
                unlink($cacheFile); // 过期删除
            }
        }
        return false;
    }

    /**
     * 根据标签清除缓存
     */
    public static function clearTag(string $tag): bool 
    {
        try {
            $tagFile = RUNTIME_PATH . 'cache/tags/' . $tag . '.tag';
            
            if (!file_exists($tagFile)) {
                return true; // 没有标签文件，认为清理成功
            }

            // 读取标签关联的所有缓存键
            $keys = json_decode(file_get_contents($tagFile), true) ?: [];
            $deletedCount = 0;

            // 删除所有相关的缓存文件
            foreach ($keys as $key) {
                $cacheFile = self::getCacheFile($key);
                if (file_exists($cacheFile)) {
                    unlink($cacheFile);
                    $deletedCount++;
                }
            }
            
            // 删除标签文件本身
            unlink($tagFile);

            self::logInfo("clearTag", "已清除标签 '{$tag}' 的缓存文件 {$deletedCount} 个");
            return true;
        } catch (\Exception $e) {
            self::logError('clearTag', $tag, $e);
            return false;
        }
    }

    /**
     * 清除指定用户的缓存
     */
    public static function clearUserCache(int $userId): bool 
    {
        return self::clearTag("user_{$userId}");
    }

    /**
     * 清除CRM客户缓存
     */
    public static function clearCustomerCache(int $customerId): bool 
    {
        return self::clearTag("crm_customer_{$customerId}");
    }

    /**
     * 清除CRM客户列表缓存
     */
    public static function clearCustomerListCache(): bool 
    {
        return self::clearTag("crm_customer_list");
    }

    /**
     * 获取缓存文件路径
     */
    private static function getCacheFile(string $key): string 
    {
        $safeKey = preg_replace('/[^a-zA-Z0-9_]/', '_', $key);
        return RUNTIME_PATH . 'cache/' . $safeKey . '.cache';
    }

    /**
     * 记录缓存统计
     */
    public static function logCacheHit(string $operation, string $key, bool $hit): void 
    {
        try {
            $stats = [
                'timestamp' => date('Y-m-d H:i:s'),
                'operation' => $operation,
                'key' => $key,
                'hit' => $hit
            ];
            
            file_put_contents(self::STATS_FILE, json_encode($stats) . "\n", FILE_APPEND | LOCK_EX);
        } catch (\Exception $e) {
            // 静默失败，不影响主流程
        }
    }

    /**
     * 获取缓存统计信息
     */
    public static function getCacheStats(): array 
    {
        try {
            if (!file_exists(self::STATS_FILE)) {
                return [
                    'total_requests' => 0,
                    'cache_hits' => 0,
                    'cache_misses' => 0,
                    'hit_rate' => 0
                ];
            }

            $lines = file(self::STATS_FILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
            $total = $hits = $misses = 0;

            foreach ($lines as $line) {
                $stats = json_decode($line, true);
                if ($stats && isset($stats['hit'])) {
                    $total++;
                    if ($stats['hit']) {
                        $hits++;
                    } else {
                        $misses++;
                    }
                }
            }

            return [
                'total_requests' => $total,
                'cache_hits' => $hits,
                'cache_misses' => $misses,
                'hit_rate' => $total > 0 ? round($hits / $total * 100, 2) : 0
            ];
        } catch (\Exception $e) {
            return [
                'total_requests' => 0,
                'cache_hits' => 0,
                'cache_misses' => 0,
                'error' => '获取缓存统计失败'
            ];
        }
    }

    /**
     * 记录错误日志
     */
    private static function logError(string $operation, string $key, \Exception $e): void 
    {
        try {
            $error = [
                'timestamp' => date('Y-m-d H:i:s'),
                'level' => 'ERROR',
                'operation' => $operation,
                'key' => $key,
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine()
            ];
            
            $logFile = RUNTIME_PATH . 'logs/cache_errors.log';
            $logDir = dirname($logFile);
            if (!is_dir($logDir)) {
                mkdir($logDir, 0755, true);
            }
            
            file_put_contents($logFile, json_encode($error, JSON_UNESCAPED_UNICODE) . "\n", FILE_APPEND | LOCK_EX);
        } catch (\Exception $ex) {
            // 静默失败
        }
    }

    /**
     * 记录信息日志
     */
    private static function logInfo(string $operation, string $message): void 
    {
        try {
            $info = [
                'timestamp' => date('Y-m-d H:i:s'),
                'level' => 'INFO',
                'operation' => $operation,
                'message' => $message
            ];
            
            $logFile = RUNTIME_PATH . 'logs/cache_info.log';
            $logDir = dirname($logFile);
            if (!is_dir($logDir)) {
                mkdir($logDir, 0755, true);
            }
            
            file_put_contents($logFile, json_encode($info, JSON_UNESCAPED_UNICODE) . "\n", FILE_APPEND | LOCK_EX);
        } catch (\Exception $e) {
            // 静默失败
        }
    }

    /**
     * 智能缓存方法 - 只有在缓存不存在时才执行回调
     */
    public static function remember(string $key, int $expire, callable $callback) 
    {
        // 先检查本地缓存
        $cached = self::getLocalCache($key);
        if ($cached !== null) {
            self::logCacheHit('remember', $key, true);
            return $cached;
        }

        // 缓存不存在，执行回调
        self::logCacheHit('remember', $key, false);
        $data = $callback();
        
        if ($data !== null) {
            self::setLocalCache($key, $data, $expire);
        }
        
        return $data;
    }

    /**
     * 智能缓存功能（支持标签系统）
     */
    public static function smartCache(string $key, callable $callback, array $options = []) 
    {
        $ttl = $options['ttl'] ?? 3600;
        $tags = $options['tags'] ?? []; // 支持指定要添加的标签
        $cached = self::getLocalCache($key);
        
        if ($cached !== null) {
            // 缓存命中，返回缓存数据
            $result = [
                'cached' => true,
                'data' => $cached,
                'hit' => true
            ];
            self::logCacheHit('smartCache', $key, true);
            return $result;
        } else {
            // 缓存未命中，执行回调并缓存结果
            $data = $callback();
            $result = [
                'cached' => false,
                'data' => $data,
                'hit' => false
            ];
            
            if ($data !== null) {
                self::setLocalCache($key, $data, $ttl);
                
                // 为缓存添加标签关联（如果指定了标签）
                if (!empty($tags) && is_array($tags)) {
                    foreach ($tags as $tag) {
                        self::addTag($tag, $key);
                    }
                }
            }
            
            self::logCacheHit('smartCache', $key, false);
            return $result;
        }
    }

    /**
     * 添加标签
     */
    public static function addTag(string $tag, string $key): bool 
    {
        try {
            // 创建一个映射文件，将标签与缓存键关联
            $tagFile = RUNTIME_PATH . 'cache/tags/' . $tag . '.tag';
            $tagDir = dirname($tagFile);
            if (!is_dir($tagDir)) {
                mkdir($tagDir, 0755, true);
            }
            
            // 读取现有标签文件
            $existingKeys = [];
            if (file_exists($tagFile)) {
                $existingKeys = json_decode(file_get_contents($tagFile), true) ?: [];
            }
            
            // 添加新键（如果不存在）
            if (!in_array($key, $existingKeys)) {
                $existingKeys[] = $key;
                file_put_contents($tagFile, json_encode($existingKeys, JSON_UNESCAPED_UNICODE), LOCK_EX);
            }
            
            return true;
        } catch (\Exception $e) {
            self::logError('addTag', $tag . '_' . $key, $e);
            return false;
        }
    }

    /**
     * 重置缓存统计
     */
    public static function resetStats(): bool 
    {
        try {
            if (file_exists(self::STATS_FILE)) {
                return unlink(self::STATS_FILE);
            }
            return true;
        } catch (\Exception $e) {
            self::logError('resetStats', 'stats_file', $e);
            return false;
        }
    }

    /**
     * 预热用户缓存
     */
    public static function warmupUserCache(int $userId, callable $callback, int $expire = 3600): bool 
    {
        try {
            $cacheKey = self::getUserCacheKey('auth_data', $userId);
            
            // 只有在缓存不存在时才进行预热
            if (!self::hasLocalCache($cacheKey)) {
                $data = $callback();
                if ($data !== null) {
                    return self::setLocalCache($cacheKey, $data, $expire);
                }
            }
            
            return true;
        } catch (\Exception $e) {
            self::logError('warmupCache', (string)$userId, $e);
            return false;
        }
    }
}
